其他
13000 字的 Spring MVC 总结
The following article is from java技术爱好者 Author 牛九木
概述
接收前端传过来Request请求。 根据映射路径找到对应的处理器处理请求,处理完成之后返回ModelAndView。 进行视图解析,视图渲染,返回响应结果。
一、搭建项目
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
二、定义Controller
2.1 实现Controller接口
/**
* @author Ye Hongzhi 公众号:java技术爱好者
* @name DemoController
* @date 2020-08-25 22:28
**/
@org.springframework.stereotype.Controller("/demo/controller")
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//业务处理
return null;
}
}
2.2 实现HttpRequestHandler接口
/**
* @author Ye Hongzhi 公众号:java技术爱好者
* @name HttpDemoController
* @date 2020-08-25 22:45
**/
@Controller("/http/controller")
public class HttpDemoController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
//业务处理
}
}
2.3 实现Servlet接口
@Controller("/servlet/controller")
public class ServletDemoController implements Servlet {
//以下是Servlet生命周期方法
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public SimpleServletHandlerAdapter simpleServletHandlerAdapter() {
return new SimpleServletHandlerAdapter();
}
}
2.4 使用@RequestMapping
@Controller
@RequestMapping("/requestMapping/controller")
public class RequestMappingController {
@RequestMapping("/demo")
public String demo() {
return "HelloWord";
}
}
2.4.1 支持Restful风格
@RequestMapping(value = "/restful", method = RequestMethod.GET)
public String get() {
//查询
return "get";
}
@RequestMapping(value = "/restful", method = RequestMethod.POST)
public String post() {
//创建
return "post";
}
@RequestMapping(value = "/restful", method = RequestMethod.PUT)
public String put() {
//更新
return "put";
}
@RequestMapping(value = "/restful", method = RequestMethod.DELETE)
public String del() {
//删除
return "post";
}
2.4.2 支持Ant风格
//匹配 /antA 或者 /antB 等URL
@RequestMapping("/ant?")
public String ant() {
return "ant";
}
//匹配 /ant/a/create 或者 /ant/b/create 等URL
@RequestMapping("/ant/*/create")
public String antCreate() {
return "antCreate";
}
//匹配 /ant/create 或者 /ant/a/b/create 等URL
@RequestMapping("/ant/**/create")
public String antAllCreate() {
return "antAllCreate";
}
2.5 使用HandlerFunction
Spring5.0
后引入的方式,主要用于做响应式接口的开发,也就是Webflux的开发。三、接收参数
3.1 接收普通参数
@RequestMapping(value = "/restful", method = RequestMethod.POST)
public String post(Integer id, String name, int money) {
System.out.println("id:" + id + ",name:" + name + ",money:" + money);
return "post";
}
3.2 @RequestParam参数名绑定
/**
* value: 参数名
* required: 是否request中必须包含此参数,默认是true。
* defaultValue: 默认参数值
*/
@RequestMapping(value = "/restful", method = RequestMethod.GET)
public String get(@RequestParam(value = "userId", required = false, defaultValue = "0") String id) {
System.out.println("id:" + id);
return "get";
}
3.3 @PathVariable路径参数
@RequestMapping(value = "/restful/{id}", method = RequestMethod.GET)
public String search(@PathVariable("id") String id) {
System.out.println("id:" + id);
return "search";
}
3.4 @RequestHeader绑定请求头属性
@RequestMapping("/head")
public String head(@RequestHeader("Accept-Language") String acceptLanguage) {
return acceptLanguage;
}
3.5 @CookieValue绑定请求的Cookie值
@RequestMapping("/cookie")
public String cookie(@CookieValue("_ga") String _ga) {
return _ga;
}
3.6 绑定请求参数到POJO对象
public class User {
private String id;
private String name;
private Integer age;
//getter、setter方法
}
@RequestMapping("/body")
public String body(User user) {
return user.toString();
}
3.6.1 支持级联属性
public class Address {
private String id;
private String name;
//getter、setter方法
}
public class User {
private String id;
private String name;
private Integer age;
private Address address;
//getter、setter方法
}
3.6.2 @InitBinder解决接收多对象时属性名冲突
//user和address都有id和name这两个属性
@RequestMapping(value = "/twoBody", method = RequestMethod.POST)
public String twoBody(User user, Address address) {
return user.toString() + "," + address.toString();
}
@InitBinder("user")
public void initBindUser(WebDataBinder webDataBinder) {
webDataBinder.setFieldDefaultPrefix("u.");
}
@InitBinder("address")
public void initBindAddress(WebDataBinder webDataBinder) {
webDataBinder.setFieldDefaultPrefix("addr.");
}
3.6.3 @Requestbody自动解析JSON字符串封装到对象
@RequestMapping(value = "/requestBody", method = RequestMethod.POST)
public String requestBody(@RequestBody User user) {
return user.toString();
}
四、参数类型转换
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
//String转换成Date类型
return sdf.parse(source);
} catch (Exception e) {
//类型转换错误
e.printStackTrace();
}
return null;
}
}
@Configuration
public class ConverterConfig extends WebMvcConfigurationSupport {
@Override
protected void addFormatters(FormatterRegistry registry) {
//添加类型转换器
registry.addConverter(new StringToDateConverter());
}
}
五、页面跳转
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>7.0.59</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
@Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
}
第三步,设置IDEA的配置。
@Controller
@RequestMapping("/view")
public class ViewController {
@RequestMapping("/hello")
public String hello() throws Exception {
return "hello";
}
}
六、@ResponseBody
//用在类、方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
@RequestMapping("/userList")
@ResponseBody
public List<User> userList() throws Exception {
List<User> list = new ArrayList<>();
list.add(new User("1","姚大秋",18));
list.add(new User("2","李星星",18));
list.add(new User("3","冬敏",18));
return list;
}
七、@ModelAttribute
7.1 用在无返回值的方法上
@Controller
@RequestMapping("/modelAttribute")
public class ModelAttributeController {
//先执行这个方法
@ModelAttribute
public void modelAttribute(Model model){
//在request域中放入数据
model.addAttribute("userName","公众号:java技术爱好者");
}
@RequestMapping("/index")
public String index(){
//跳转到inex.jsp页面
return "index";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<!-- 获取到userName属性值 -->
<h1>${userName}</h1>
</body>
</html>
7.2 放在有返回值的方法上
//放在有参数的方法上
@ModelAttribute
public User userAttribute() {
//相当于model.addAttribute("user",new User("1", "Java技术爱好者", 18));
return new User("1", "Java技术爱好者", 18);
}
@RequestMapping("/user")
public String user() {
return "user";
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>ID:${user.id}</h1>
<h1>名称:${user.name}</h1>
<h1>年龄:${user.age}岁</h1>
</body>
</html>
//自定义属性名为"u"
@ModelAttribute("u")
public User userAttribute() {
return new User("1", "Java技术爱好者", 18);
}
/**
JSP就要改成这样写:
<h1>ID:${u.id}</h1>
<h1>名称:${u.name}</h1>
<h1>年龄:${u.age}岁</h1>
*
7.3 放在RequestMapping方法上
@Controller
@RequestMapping("/modelAttribute")
public class ModelAttributeController {
@RequestMapping("/jojo")
@ModelAttribute("attributeName")
public String jojo() {
return "JOJO!我不做人了!";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1>${attributeName}</h1>
</body>
</html>
7.4 放在方法入参上
@ModelAttribute("u")
public User userAttribute() {
return new User("1", "Java技术爱好者", 18);
}
@RequestMapping("/java")
public String user1(@ModelAttribute("u") User user) {
//拿到@ModelAttribute("u")方法返回的值,打印出来
System.out.println("user:" + user);
return "java";
}
八、拦截器
preHandle():在业务处理器处理请求之前被调用。预处理。 postHandle():在业务处理器处理请求执行完成后,生成视图之前执行。后处理。 afterCompletion():在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//预处理,返回true则继续执行。如果需要登录校验,校验不通过返回false即可,通过则返回true。
System.out.println("执行preHandle()方法");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//后处理
System.out.println("执行postHandle()方法");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//在DispatcherServlet完全处理完请求后被调用
System.out.println("执行afterCompletion()方法");
}
}
@Configuration
public class ConverterConfig extends WebMvcConfigurationSupport {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
}
}
九、全局异常处理
HandlerExceptionResolver
接口的类图就知道了:DefaultHandlerExceptionResolver
,默认的异常处理器。根据各个不同类型的异常,返回不同的异常视图。SimpleMappingExceptionResolver
,简单映射异常处理器。通过配置异常类和view的关系来解析异常。ResponseStatusExceptionResolver
,状态码异常处理器。解析带有@ResponseStatus
注释类型的异常。ExceptionHandlerExceptionResolver
,注解形式的异常处理器。对@ExceptionHandler
注解的方法进行异常解析。
9.1 SimpleMappingExceptionResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面 -->
<property name="defaultErrorView" value="err"/>
<!-- 定义异常处理页面用来获取异常信息的属性名,默认名为exception -->
<property name="exceptionAttribute" value="ex"/>
<!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常也页名作为值 -->
<property name="exceptionMappings">
<props>
<!-- 异常,err表示err.jsp页面 -->
<prop key="java.lang.Exception">err</prop>
<!-- 可配置多个prop -->
</props>
</property>
</bean>
</beans>
@SpringBootApplication
@ImportResource("classpath:spring-config.xml")
public class SpringmvcApplication {
public static void main(String[] args) {
SpringApplication.run(SpringmvcApplication.class, args);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常页面</title>
</head>
<body>
<h1>出现异常,这是一张500页面</h1>
<br>
<%-- 打印异常到页面上 --%>
<% Exception ex = (Exception)request.getAttribute("ex"); %>
<br>
<div><%=ex.getMessage()%></div>
<% ex.printStackTrace(new java.io.PrintWriter(out)); %>
</body>
</html>
@Controller
@RequestMapping("/exception")
public class ExceptionController {
@RequestMapping("/index")
public String index(String msg) throws Exception {
if ("null".equals(msg)) {
//抛出空指针异常
throw new NullPointerException();
}
return "index";
}
}
9.2 ResponseStatusExceptionResolver
@ResponseStatus
注释的异常。请看演示代码:@ResponseStatus
注解修饰://HttpStatus枚举有所有的状态码,这里返回一个400的响应码
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class DefinedException extends Exception{
}
@RequestMapping("/defined")
public String defined(String msg) throws Exception {
if ("defined".equals(msg)) {
throw new DefinedException();
}
return "index";
}
9.3 ExceptionHandlerExceptionResolver
public class BaseException extends Exception {
public BaseException(String message) {
super(message);
}
}
public class ErrorInfo {
public static final Integer OK = 0;
public static final Integer ERROR = -1;
private Integer code;
private String message;
private String url;
//getter、setter
}
//这里使用了RestControllerAdvice,是@ResponseBody和@ControllerAdvice的结合
//会把实体类转成JSON格式的提示返回,符合前后端分离的架构
@RestControllerAdvice
public class GlobalExceptionHandler {
//这里自定义了一个BaseException,当抛出BaseException异常就会被此方法处理
@ExceptionHandler(BaseException.class)
public ErrorInfo errorHandler(HttpServletRequest req, BaseException e) throws Exception {
ErrorInfo r = new ErrorInfo();
r.setMessage(e.getMessage());
r.setCode(ErrorInfo.ERROR);
r.setUrl(req.getRequestURL().toString());
return r;
}
}
@RequestMapping("/base")
public String base(String msg) throws Exception {
if ("base".equals(msg)) {
throw new BaseException("测试抛出BaseException异常,欧耶!");
}
return "index";
}
絮叨
https://github.com/yehongzhi/mall
#投 稿 通 道#
让你的博客被更多人看到
自己辛苦写的博客没人看?想让自己的作品被更多人看到?或者利用写作赚点外快?快来投稿吧。
Java后端 鼓励读者朋友在我们的平台上分享各类优质内容,可以是技术博客,也可以是面试经验或图文教材。我们的目的只有一个,让知识真正流动起来。
📝 来稿标准:
• 稿件确系个人原创作品
• 如果文章并非首发,请在投稿时提醒并附上所有已发布链接
• 如作者同意 Java后端 标记“原创”标志,将会提供 100-200 或更高的稿费
• 如果文章已在别的公众号以原创的方式发布,请给 Java后端 开白名单
📬 投稿方式:
• 投稿请联系:web527zsd
• 添加好友时,请备注投稿
- END -
最近整理一份面试资料《Java技术栈学习手册》,覆盖了Java技术、面试题精选、Spring全家桶、Nginx、SSM、微服务、数据库、数据结构、架构等等。
获取方式:点“ 在看,关注公众号 Java后端 并回复 777 领取,更多内容陆续奉上。